package com.ruihu.admin.module.support.task.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.ruihu.admin.common.constant.JudgeEnum;
import com.ruihu.admin.common.exception.BusinessException;
import com.ruihu.admin.config.AIImagePath;
import com.ruihu.admin.constant.CommonConst;
import com.ruihu.admin.module.business.log.aiimagelog.dao.AiImageLogDao;
import com.ruihu.admin.module.business.log.aiimagelog.domain.dto.AiImageLogDTO;
import com.ruihu.admin.module.business.log.aiimagelog.domain.dto.AiImageLogQueryDTO;
import com.ruihu.admin.module.business.log.interfacelog.constant.InterfaceModuleEnum;
import com.ruihu.admin.module.business.log.interfacelog.domain.entity.InterfaceLogEntity;
import com.ruihu.admin.module.business.log.interfacelog.service.InterfaceLogService;
import com.ruihu.admin.module.business.task.dao.SubTaskDao;
import com.ruihu.admin.module.business.task.domain.entity.SubTaskEntity;
import com.ruihu.admin.module.business.tasknode.service.AIInterfaceService;
import com.ruihu.admin.module.business.tasknode.manager.TaskNodeInstManager;
import com.ruihu.admin.module.support.file.constant.FileModuleTypeEnum;
import com.ruihu.admin.module.support.task.domain.AIErrorFile;
import com.ruihu.admin.module.support.task.domain.AIImageDescDTO;
import com.ruihu.admin.module.support.task.domain.AIImageDescResultDTO;
import com.ruihu.admin.module.system.systemconfig.SystemConfigService;
import com.ruihu.admin.module.system.systemconfig.constant.SystemConfigEnum;
import com.ruihu.admin.module.system.systemconfig.domain.dto.SystemConfigDTO;
import com.ruihu.admin.third.ApplicationContext;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AI图片扫描任务
 */
@Slf4j
public class AIImageDyTask implements DyTask {

    public static final int DURATION_DAY = 1;
    private InterfaceLogEntity interfaceLogEntity;
    private Long expirationTime;
    private SystemConfigService systemConfigService;
    private InterfaceLogService interfaceLogService;
    private AiImageLogDao aiImageLogDao;
    private DyTaskService dyTaskService;
    private TaskNodeInstManager taskNodeInstManager;
    private AIImagePath aiImageBasePath;


    public AIImageDyTask(InterfaceLogEntity interfaceLogEntity) {
        if (null == interfaceLogEntity)
            throw new IllegalArgumentException("interfaceLogEntity must not be null");
        this.interfaceLogEntity = interfaceLogEntity;
        Date beginDate = interfaceLogEntity.getCreateTime();
        Map<String, Object> param = this.interfaceLogEntity.getParam();
        if (null == param)
            param = new HashMap<>();

        if (!param.containsKey("electricCoreCode")) {
            throw new BusinessException("electricCoreCode must not be null");
        }
        if (!param.containsKey("poleGroupNumber")) {
            throw new BusinessException("poleGroupNumber must not be null");
        }
        if (!param.containsKey("date")) {
            param.put("date", DateUtil.format(beginDate, DatePattern.PURE_DATE_FORMAT));
        }

        this.interfaceLogEntity = interfaceLogEntity;
        this.dyTaskService = ApplicationContext.getBean(DyTaskService.class);
        this.systemConfigService = ApplicationContext.getBean(SystemConfigService.class);
        this.interfaceLogService = ApplicationContext.getBean(InterfaceLogService.class);
        this.taskNodeInstManager = ApplicationContext.getBean(TaskNodeInstManager.class);
        this.aiImageLogDao = ApplicationContext.getBean(AiImageLogDao.class);

        // 读取轮询任务的超时时长
        SystemConfigDTO systemConfigDTO = this.systemConfigService.getCacheByKey(SystemConfigEnum.Key.AI_INTERFACE_TIME_OFFSET);
        if (Objects.isNull(systemConfigDTO) || StrUtil.isEmpty(systemConfigDTO.getConfigValue())) {
            this.expirationTime = DateUtil.offsetDay(beginDate, DURATION_DAY).getTime();
        } else {
            String configValue = systemConfigDTO.getConfigValue();
            String offset = ReUtil.get("\\d+", configValue, 0);
            if (StrUtil.isNotEmpty(offset)) {
                if (configValue.endsWith("d")) {
                    this.expirationTime = DateUtil.offsetDay(beginDate, Integer.valueOf(offset)).getTime();
                } else if (configValue.endsWith("h")) {
                    this.expirationTime = DateUtil.offsetHour(beginDate, Integer.valueOf(offset)).getTime();
                } else if (configValue.endsWith("m")) {
                    this.expirationTime = DateUtil.offsetMinute(beginDate, Integer.valueOf(offset)).getTime();
                } else if (configValue.endsWith("s")) {
                    this.expirationTime = DateUtil.offsetSecond(beginDate, Integer.valueOf(offset)).getTime();
                }
            }
        }
        this.aiImageBasePath = ApplicationContext.getBean(AIImagePath.class);
    }

    @Override
    public void run() {
        log.info("Wait for AI result images >>>> interface log id {}, poleGroupNumber {}", this.interfaceLogEntity.getId(), this.interfaceLogEntity.getPoleGroupNumber());

        try {
            if (DateUtil.current() < this.expirationTime) {
                // 判断扫描目录是否存在，如果存在直接取
                if (this.interfaceLogEntity.getParam().containsKey("scanPath")) {
                    String scanPath = (String) this.interfaceLogEntity.getParam().get("scanPath");
                    this.scanAIImages(scanPath);
                } else {
                    // 扫描当天目录
                    String electricCoreCode = (String) this.interfaceLogEntity.getParam().get("electricCoreCode");
                    String date = DateUtil.format(DateUtil.date().toJdkDate(), DatePattern.PURE_DATE_FORMAT);
                    String tmpPath = date + File.separator + electricCoreCode;
                    File electricPath = new File(this.aiImageBasePath.getPath() + File.separator + tmpPath);
                    if (electricPath.exists()) {
                        File[] poleGroupNumberFileList = electricPath.listFiles();
                        if (ArrayUtil.isNotEmpty(poleGroupNumberFileList)) {
                            for (File file : poleGroupNumberFileList) {
                                // 找到了目标极组目录
                                if (file.isDirectory() && file.getName().equals(this.interfaceLogEntity.getPoleGroupNumber())) {
                                    String scanPath = tmpPath + File.separator + this.interfaceLogEntity.getPoleGroupNumber();
                                    this.interfaceLogEntity.getParam().put("scanPath", scanPath);
                                    this.interfaceLogService.update(this.interfaceLogEntity);
                                    this.interfaceLogEntity = this.interfaceLogService.getById(this.interfaceLogEntity.getId());
                                    this.scanAIImages(scanPath);
                                }
                            }
                        }
                    }
                }
            } else {
                interfaceLogEntity.setResult(JudgeEnum.NO.getValue());
                interfaceLogEntity.setStatus(JudgeEnum.YES.getValue().toString());
                interfaceLogEntity.setCompletionTime(new Date());
                interfaceLogEntity.setFailReason("任务已经超时");
                this.interfaceLogService.update(interfaceLogEntity);
                this.dyTaskService.cancelTask(this.interfaceLogEntity.getId());
            }
        } catch (Exception ex) {
            log.error("Dispose AI image error", ex);
        }
    }

    private void scanAIImages(String scanPath) {
        // 检测是否存在描述文件
        String basePath = this.aiImageBasePath.getPath() + File.separator + scanPath;
        File descFile = new File(basePath + File.separator + CommonConst.DESC_FILE_NAME);
        if (FileUtil.exist(descFile)) {
            // 解析描述文件
            log.info("The AI result file desc.txt has been found. >>> {}", descFile.getAbsolutePath());
            log.info("Start parse the file desc.txt. >>> {}", descFile.getAbsolutePath());
            String content = FileUtil.readString(descFile, Charset.forName(CharsetUtil.UTF_8));
            if (StrUtil.isEmpty(content)) {
                log.info("The AI result file desc.txt is empty. >>> {}", descFile.getAbsolutePath());
            } else {
                AIImageDescDTO aiImageDescDTO = JSON.parseObject(content, AIImageDescDTO.class);
                List<AIImageDescResultDTO> result = aiImageDescDTO.getResult();
                log.info("The AI result file has {} results. >>> {}", result.size(), descFile.getAbsolutePath());
                List<AIErrorFile> aiErrorFileList = new ArrayList<>(result.size());

                AiImageLogQueryDTO aiImageLogQueryDTO = new AiImageLogQueryDTO();
                aiImageLogQueryDTO.setInterfaceLogId(this.interfaceLogEntity.getId());
                List<AiImageLogDTO> aiImageLogDTOList = this.aiImageLogDao.queryList(aiImageLogQueryDTO);
                Set<String> successFileNameSet = aiImageLogDTOList.stream().map(AiImageLogDTO::getFileName).collect(Collectors.toSet());
                boolean isAllFileFinish = isAllFileFinish(successFileNameSet, result);
                if (isAllFileFinish) {
                    this.doAfterAllFileFinish(scanPath);
                } else {
                    result.forEach(e -> {
                        File imageFile = new File(basePath + File.separator + e.getFileName());
                        if (imageFile.exists() && !successFileNameSet.contains(e.getFileName())) {
                            AIErrorFile aiErrorFile = new AIErrorFile();
                            aiErrorFile.setFile(imageFile);
                            aiErrorFile.setDesc(e.getDesc());
                            aiErrorFile.setDefectType(e.getDefectType());
                            aiErrorFile.setElectricCoreCode((String) this.interfaceLogEntity.getParam().get("electricCoreCode"));
                            aiErrorFile.setPoleGroupNumber(this.interfaceLogEntity.getPoleGroupNumber());
                            aiErrorFileList.add(aiErrorFile);
                        } else {
                            log.warn("The file '{}' of uploaded by AI System was not found.", imageFile.getAbsolutePath());
                        }
                    });

                    if (aiErrorFileList.size() > 0) {
                        try {
                            this.taskNodeInstManager.copyAIImage(aiErrorFileList, this.interfaceLogEntity);
                        } catch (Exception ex) {
                            log.error("Copy file error", ex);
                        }
                    }
                }
            }
        }
    }

    /**
     * 检测描述文件中的文件是否全部处理完成
     * @param successFileNameSet
     * @param descImageList
     * @return
     */
    private boolean isAllFileFinish(Set<String> successFileNameSet, List<AIImageDescResultDTO> descImageList) {
        for (AIImageDescResultDTO descImage : descImageList) {
            if (!successFileNameSet.contains(descImage.getFileName()))
                return false;
        }

        return true;
    }

    /**
     * 所有文件完成处理后，更新接口日志状态，移动文件到备份目录
     * @param scanPath
     */
    private void doAfterAllFileFinish(String scanPath) {
        try {
            // 文件处理完毕，更新接口任务日志
            interfaceLogEntity.setResult(JudgeEnum.YES.getValue());
            interfaceLogEntity.setStatus(JudgeEnum.YES.getValue().toString());
            interfaceLogEntity.setCompletionTime(new Date());
            this.interfaceLogService.update(interfaceLogEntity);
            // 将处理好的文件移动到备份目录
            File[] files = new File(this.aiImageBasePath.getPath() + File.separator + scanPath).listFiles();
            for (File file : files) {
                // 拷贝描述文件到目标目录
                if (CommonConst.DESC_FILE_NAME.equals(file.getName())) {
                    String destBasePath = ApplicationContext.getApplicationContext().getEnvironment().getProperty("file-upload-service.path");
                    SubTaskDao subTaskDao = ApplicationContext.getBean(SubTaskDao.class);
                    SubTaskEntity subTaskEntity = subTaskDao.selectById(this.interfaceLogEntity.getSubTaskId());
                    if (Objects.nonNull(subTaskEntity)) {
                        File descDestFile = new File(destBasePath + File.separator
                                + FileModuleTypeEnum.TASK_NODE.getPath()
                                + File.separator + subTaskEntity.getTaskCode()
                                + File.separator + this.interfaceLogEntity.getElectricCoreCode()
                                + File.separator + this.interfaceLogEntity.getPoleGroupNumber());
                        FileUtil.copy(file, descDestFile, true);
                    }
                }
                File destFile = new File(this.aiImageBasePath.getBackupPath() + File.separator + scanPath + File.separator + file.getName());
                FileUtil.move(file, destFile, true);

                log.info("Move the file success >>> electric core code {}, source desc file {}, dest desc file {}",
                        this.interfaceLogEntity.getParam().get("electricCoreCode"),
                        file.getAbsolutePath(),
                        destFile.getAbsolutePath());
            }
            log.info("Complete the AI images >>> electric core code {}, source desc file {}, dest desc file {}",
                    this.interfaceLogEntity.getParam().get("electricCoreCode"));

            // 发送清除消息
            if (ApplicationContext.isProductEnvironment()) {
                AIInterfaceService aiInterfaceManager = ApplicationContext.getBean(AIInterfaceService.class);
                aiInterfaceManager.sendPoleGroupMessageToAI(this.interfaceLogEntity.getMainTaskId(), this.interfaceLogEntity.getSubTaskId(),
                        this.interfaceLogEntity.getElectricCoreCode(), this.interfaceLogEntity.getPoleGroupNumber(),
                        InterfaceModuleEnum.SEND_UPDATE_POLE_GROUP_MSG_TO_AI, 1);
            }
            // 取消定时任务
            this.dyTaskService.cancelTask(this.interfaceLogEntity.getId());
        } catch (Exception ex) {
            log.error("Update AI interface log or cancel task error or move desc.txt file error", ex);
        }
    }
}
